import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

import '../../page/scrawl_widget.dart';
import '../../path/dash_path.dart';
import 'image_queue.dart';
import 'math_util.dart';
import 'pen.dart';
import 'pen_style.dart';

enum TouchEvent {
  idle,
  downEvent,
  moveEvent,
  upEvent,
}

/// 关于绘制不支持虚线的官方回复 https://github.com/flutter/flutter/issues/4858
/// 绘制矢量图时，第一个点不做绘制，非矢量图都会绘制
/// todo 阴影的实现
class ScrawlPen extends BasePen {
  Paint strokeLinePaint,
      linePaint,
      imagePaint,
      clearPaint,
      shadowPaint; //对应描边,线条,图片的画笔

  /// 线条参数
  Path path = Path(); //线条的路径

  /// 绘制过程所需参数
  ScrawlPoint prePoint, preImagePoint; //滑动最后的点和最后一个绘制的带图的点
  ImageQueue imageQueue; //一个图片队列,用于取依次去每个图
  List<ScrawlPoint> drawPoints = []; //最终绘制点的集合

  Color colorShadow;
  final PenStyle style;
  final String brushId;

  bool isTape;
  bool isImage;
  bool isStroke;
  bool isShadow;

  ScrawlPen({
    @required this.brushId,
    @required this.style,
  }) {
    isTape = style.lineWidth != null && style.lineWidth > 0;
    isStroke = style.strokeLineWidth != null && style.strokeLineWidth > 0;
    isShadow = style.shadowRadius != null && style.shadowRadius > 0;

    if (style.scrawlImages != null && style.scrawlImages.length > 0) {
      imageQueue = ImageQueue(style.scrawlImages);
      isImage = true;
    } else {
      isImage = false;
    }

    ImageShader shader;
    if (style.tapeImage != null) {
      shader = ImageShader(
        style.tapeImage,
        TileMode.repeated,
        TileMode.repeated,
        Matrix4.identity().storage,
      );
    }

    Color colorLine;
    try {
      colorLine = Color(style.lineColor);
    } catch (e, s) {
      debugPrint(s.toString());
      colorLine = Color(0x00000000);
    }

    Color colorStroke;
    try {
      colorStroke = Color(style.strokeLineColor);
    } catch (e, s) {
      debugPrint(s.toString());
      colorStroke = Color(0x00000000);
    }

    try {
      colorShadow = Color(style.shadowColor);
    } catch (e, s) {
      debugPrint(s.toString());
      colorShadow = Color(0x00000000);
    }

    linePaint = Paint()
      ..isAntiAlias = true
      ..strokeJoin = StrokeJoin.round
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..color = colorLine
      ..strokeWidth = style.lineWidth * 1.0; //抗锯齿
    if (isTape && shader != null) {
      linePaint.color = Color(0xFFFFFFFF);
      linePaint.shader = shader;
    }

    strokeLinePaint = Paint()
      ..isAntiAlias = true
      ..strokeJoin = StrokeJoin.round
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..color = colorStroke
      ..strokeWidth = style.strokeLineWidth * 1.0;

    shadowPaint = Paint()
      ..isAntiAlias = true
      ..strokeJoin = StrokeJoin.round
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..color = colorShadow
      ..colorFilter
      ..maskFilter = MaskFilter.blur(BlurStyle.outer, style.shadowRadius / 2)
      ..strokeWidth = style.lineWidth * 1.0;

    imagePaint = Paint()
      ..isAntiAlias = true
      ..color = Color(0xFFFFFFFF)
      ..strokeJoin = StrokeJoin.round
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke
      ..strokeWidth = DEFAULT_STROKE_WIDTH;

    clearPaint = Paint()..blendMode = BlendMode.clear;
  }

  @override
  void draw(Canvas canvas, {bool isRedraw = false}) {
    //撤销或恢复操作
    if (path != null) {
      if (isShadow) {
        canvas.drawPath(path, shadowPaint);
      }
      if (isStroke) {
        canvas.drawPath(path, strokeLinePaint);
      }
      if (isTape) {
        canvas.drawPath(
            style.dashSpacing > 0
                ? dashPath(
                    path,
                    dashArray: CircularIntervalList(
                        [style.dashWidth, style.dashSpacing]),
                  )
                : path,
            linePaint);
      }
    }

    if (isImage) {
      if (drawPoints != null && drawPoints.length > 0) {
        for (ScrawlPoint point in drawPoints) {
          if (point?.image != null) {
            canvas.save();
//            canvas.translate(ScreenUtil.getInstance().screenWidth / 2,
//                ScreenUtil.getInstance().screenHeight / 2);

            canvas.translate(point.getTranslateX(), point.getTranslateY());
            if (style.isVector) {
              canvas.translate(-point.offsetX, -point.offsetY);
              canvas.rotate(point.rotate);
              canvas.translate(point.offsetX, point.offsetY);
            }
            canvas.scale(point.scale);
            canvas.drawImage(point.image, Offset.zero, imagePaint);
            canvas.restore();
          }
        }
      }
    }
  }

  @override
  bool downEvent(Offset touchPoint) {
    drawPoints.clear();
    double x = touchPoint.dx;
    double y = touchPoint.dy;

    path.moveTo(x, y);
    ScrawlPoint point = ScrawlPoint(x: x, y: y);

    if (isImage && !style.isVector) {
      //获取图片队列当前图片
      ui.Image currentImage = imageQueue.getCurrentBitmap();
      if (currentImage != null) {
        int imageWidth = currentImage.width;
        int imageHeight = currentImage.height;
        double scaleH = style.imageWidth / imageHeight;
        double scaleW = style.imageWidth / imageWidth;
        double scale = min(scaleH, scaleW);

        point.scale = scale;
        point.offsetX = -style.imageWidth / 2;
        point.offsetY = -style.imageWidth / 2;
        point.image = currentImage;
      }
    }
    preImagePoint = point;
    prePoint = point;
    drawPoints.add(point);

    return true;
  }

  @override
  bool moveEvent(Offset touchPoint) {
    ScrawlPoint point;
    bool isChanged = false;

    double x = touchPoint.dx;
    double y = touchPoint.dy;
    final double dx = (x - prePoint.x).abs();
    final double dy = (y - prePoint.y).abs();
    //两点之间的距离大于等于3时，生成贝塞尔绘制曲线
    if (dx >= 3 || dy >= 3) {
      //设置贝塞尔曲线的操作点为起点和终点的一半
      double cX = (x + prePoint.x) / 2;
      double cY = (y + prePoint.y) / 2;

      //二次贝塞尔，实现平滑曲线；preX, preY为操作点，cX, cY为终点
      path.quadraticBezierTo(prePoint.x, prePoint.y, cX, cY);
      point = ScrawlPoint(x: x, y: y);
      isChanged = true;
    }
    //判断是否需要在该点绘制图形
    bool canDrawPoint() {
      if (preImagePoint.scale == null) return true;

      double s =
          preImagePoint.scale * imageQueue.getSpacing() * 2 + style.spacing;
      return isDrawPoint(preImagePoint.x, preImagePoint.y, x, y, s);
    }

    if (isImage && canDrawPoint()) {
      //获取图片队列当前图片
      ui.Image currentImage = imageQueue.getCurrentBitmap();
      if (currentImage != null) {
        int imageWidth = currentImage.width;
        int imageHeight = currentImage.height;
        double scaleH = style.imageWidth / imageHeight;
        double scaleW = style.imageWidth / imageWidth;
        double scale = min(scaleH, scaleW);

        if (point == null) {
          point = ScrawlPoint(x: x, y: y);
        }
        point.scale = scale;
        if (style.isVector) {
          //是否为矢量涂鸦
          point.rotate =
              MathUtil.getLinesAngle(preImagePoint.x, preImagePoint.y, x, y);
        }
        point.offsetX = -style.imageWidth / 2;
        point.offsetY = -style.imageWidth / 2;
        point.image = currentImage;
        preImagePoint = point;
        isChanged = true;
      }
    }
    if (point != null) {
      drawPoints.add(point);
      prePoint = point;
    }
    if (isChanged) {
      return true;
    } else {
      return false;
    }
  }

  @override
  bool upEvent(Offset touchPoint) {
    return true;
  }

  @override
  String getBrushId() {
    return this.brushId;
  }

  /// 判断点是否存在圆内
  bool isDrawPoint(double x0, double y0, double x1, double y1, double spacing) {
    double targetDistance =
        sqrt(((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0)).abs());
    return targetDistance > spacing;
  }
}

class ScrawlPoint {
  double scale;
  double rotate = 0.0;
  double x;
  double y;
  double offsetX;
  double offsetY;
  ui.Image image;

  ScrawlPoint({
    this.scale,
    this.rotate,
    this.x,
    this.y,
    this.offsetX,
    this.offsetY,
    this.image,
  });

  double getTranslateX() {
    return x + offsetX;
  }

  double getTranslateY() {
    return y + offsetY;
  }
}
